package com.northpool.operator.http.runtime;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.JAXBUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.northpool.operator.bean.ParamBean;
import com.northpool.operator.common.algorithm.AbstractAlgorithm;
import com.northpool.operator.common.algorithm.Helper;
import com.northpool.operator.http.IHttpAlgorithm;
import com.northpool.operator.http.bean.*;
import northpool.operator.el.El;
import northpool.operator.el.ElContext;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Deprecated
public class HttpOperatorApp extends AbstractAlgorithm implements IHttpAlgorithm {

    static final Pattern varPattern = Pattern.compile("\\$.[\\w.\\[\\]]+");
    static Boolean isRunning = true;
    static String curProgress = null;

    @Override
    public void execute(String[] args) {
        // 第一个参数是http算子描述文件路径 第二个参数是流程引擎的参数
        String workPath = System.getProperty("user.dir");
        logInfo("算子工作目录：" + workPath);
        String httpOperatorXmlPath = completeRelativePath(args[0], workPath);
        String flowParamXmlPath = completeRelativePath(args[1], workPath);

        String httpXMlStr = FileUtil.readString(FileUtil.file(httpOperatorXmlPath), StandardCharsets.UTF_8);
        File flowXmlFile = new File(flowParamXmlPath);
        Map<String, String> flowParams = Helper.parseXMLFile(flowXmlFile);

        // 参数赋值
        ElContext elContext = new ElContext();
        HttpOperatorBean opBean = JAXBUtil.xmlToBean(httpXMlStr, HttpOperatorBean.class);
        StartApiBean startApi = opBean.getStartApi();
        List<ExpParamBean> startParam = startApi.getStartParam();
        List<ParamBean> inParams = startParam.stream().filter(p -> "in".equalsIgnoreCase(p.getInout())).collect(Collectors.toList());
        for (ParamBean param : inParams) {
            String name = param.getName();
            if (flowParams.containsKey(name) && flowParams.get(name) != null) {
                String value = flowParams.get(name);
                // @bcg 特殊处理遥感的错误转义, 后面一定要删除
                if (value.startsWith("http://") || value.startsWith("\"http://")) {
                    param.setValue(value);
                    elContext.add(name, value);
                } else {
                    // @bcg 适配遥感算子平台错误的参数输入格式，临时解决，后面删除
                    value = value.replace("&quot;", "\"");
                    // 值是要放到body体里面的，所以需要先转义
                    String escapedValue = JSON.toJSONString(value, SerializerFeature.BrowserCompatible);
                    if (escapedValue.startsWith("\"") && escapedValue.endsWith("\"")) {
                        escapedValue = escapedValue.substring(1, escapedValue.length() - 1);
                    }
                    param.setValue(escapedValue);
                    elContext.add(name, escapedValue);
                    logInfo("转义参数: " + escapedValue);
                }

            }
        }

        // 处理startApi表达式
        String url = startApi.getUrl();
        El startUrlEL = new El("`" + url + "`");
        if (!startUrlEL.getVarList().isEmpty()) {
            startApi.setUrl((String) startUrlEL.eval(elContext));
        }

        if (StrUtil.isNotBlank(startApi.getBody())) {
            String eval = (String) El.eval(elContext, "`" + startApi.getBody() + "`");
            // 给超图适配的，一级节点是空就不传这个值
            JSONObject bodyObject = JSONObject.parseObject(eval);
            Iterator<String> iterator = bodyObject.keySet().iterator();
            // 如果有空值就删除
            iterator.forEachRemaining(key -> {
                if (StrUtil.isEmpty(bodyObject.getString(key))) {
                    iterator.remove();
                }
            });

            startApi.setBody(eval);
        }

        List<HeaderBean> headerList = startApi.getHeaders();
        Map<String, String> startHeaderMap = headerList.stream().collect(Collectors.toMap(HeaderBean::getName, HeaderBean::getValue));

        if (!headerList.isEmpty()) {
            for (HeaderBean headerBean : headerList) {
                logInfo("statusApi.header." + headerBean.toString());
            }
        }

        logInfo("startApi.method: " + startApi.getMethod().name());
        logInfo("startApi.url: " + startApi.getUrl());
        logInfo("startApi.body: " + startApi.getBody());

        // 发送启动接口
        Method method = Method.valueOf(startApi.getMethod().name());
        HttpRequest httpRequest = HttpUtil.createRequest(method, url).addHeaders(startHeaderMap);
        httpRequest = httpRequest.timeout(startApi.getTimeout());
        if (StrUtil.isNotBlank(startApi.getBody())) {
            httpRequest.body(startApi.getBody());
        }

        HttpResponse startRes;
        try {
            startRes = httpRequest.execute();
            logInfo("startApi.response.status: " + startRes.getStatus());
            logInfo("startApi.response.body: " + startRes.body());
        } catch (Exception ex) {
            logError("算子启动异常: " + ex.getMessage());
            return;
        }

        JSONObject startReObj = getHttpResponse(startRes);
        List<ExpParamBean> outParams = startParam.stream().filter(p -> "out".equalsIgnoreCase(p.getInout())).collect(Collectors.toList());
        for (ExpParamBean param : outParams) {
            String exp = param.getExp();
            Object eval = JSONPath.eval(startReObj, exp);
            if (eval != null) {
                String val = eval.toString();
                elContext.add(param.getName(), val);
                logResult(param.getName(), val);
            } else {
                throw new RuntimeException("算子启动，结果抽取失败: " + exp);
            }
        }

        if (opBean.getStatusApi() != null) {
            // 状态APi
            try {
                startStatusApi(opBean, elContext).get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
                logError(e.getMessage());
                failedExit();
            }
        }
    }

    private static Future startStatusApi(HttpOperatorBean opBean, ElContext elContext) {
        StatusApiBean statusApi = opBean.getStatusApi();
        String url = (String) El.eval(elContext, "`" + statusApi.getUrl() + "`");
        Method method = Method.valueOf(statusApi.getMethod().name());

        if (StrUtil.isNotBlank(statusApi.getBody())) {
            String eval = (String) El.eval(elContext, "`" + statusApi.getBody() + "`");
            statusApi.setBody(eval);
        }
        HttpRequest httpRequest = HttpUtil.createRequest(method, url);
        List<HeaderBean> headerList = statusApi.getHeaders();
        if (headerList != null) {
            Map<String, String> startHeaderMap = headerList.stream().collect(Collectors.toMap(HeaderBean::getName, HeaderBean::getValue));
            httpRequest.addHeaders(startHeaderMap);

            for (HeaderBean headerBean : headerList) {
                logInfo("statusApi.header." + headerBean.toString());
            }
        }

        logInfo("statusApi.method: " + statusApi.getMethod().name());
        logInfo("statusApi.url: " + url);
        logInfo("statusApi.body: " + statusApi.getBody());

        httpRequest = httpRequest.timeout(statusApi.getTimeout());
        HttpRequest finalHttpRequest = httpRequest;

        Long maxTime = System.currentTimeMillis() + opBean.getMaxtime();
        return ThreadUtil.execAsync(() -> {
            while (isRunning) {
                if (System.currentTimeMillis() > maxTime) {
                    isRunning = false;
                    throw new RuntimeException("超过任务执行的最大时间，异常退出");
                }
                try {
                    getProgress(opBean, elContext);
                    ElContext context = new ElContext();
                    HttpResponse res = finalHttpRequest.execute();
                    logInfo("statusApi.response.status: " + res.getStatus());
                    logInfo("statusApi.response.body: " + res.body());
                    JSONObject resObj = getHttpResponse(res);
                    String successExp = statusApi.getSuccessExp();
                    String faultExp = statusApi.getFaultExp();
                    if (resObj == null) {
                        logError("算子状态返回为空");
                        TimeUnit.MILLISECONDS.sleep(statusApi.getInterval());
                        continue;
                    }
                    List<String> successGroups = ReUtil.getAllGroups(varPattern, successExp);
                    for (String path : successGroups) {
                        String val = JSONPath.eval(resObj, path).toString();
                        String param = "\"${" + path + "}\"";
                        successExp = "`" + successExp.replace(path, param) + "`";
                        context.add(path, val);
                    }
                    String successEval = (String) El.eval(context, successExp);
                    logInfo("算子状态返回成功表达式: " + successEval);
                    if (El.eval(successEval).equals(true)) {
                        isRunning = false;
                        String log = getLog(opBean, elContext);
                        logInfo(log);
                        getResult(opBean, elContext); // 抽取任务结果到流程参数中
                        logInfo("算子执行成功");
                        successExit();
                        return;
                    } else {
                        List<String> faultGroups = ReUtil.getAllGroups(varPattern, faultExp);
                        for (String path : faultGroups) {
                            String val = JSONPath.eval(resObj, path).toString();
                            String param = "\"${" + path + "}\"";
                            faultExp = "`" + faultExp.replace(path, param) + "`";
                            context.add(path, val);
                        }
                        String faultEval = (String) El.eval(context, faultExp);
                        logInfo("算子状态返回失败表达式: " + faultEval);
                        if (El.eval(faultEval).equals(true)) {
                            logError("算子任务状态返回为失败: " + faultExp);
                            isRunning = false;
                            failedExit();
                            return;
                        }
                    }

                    TimeUnit.MILLISECONDS.sleep(statusApi.getInterval());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    public static void getProgress(HttpOperatorBean opBean, ElContext elContext) {
        ProgressApi progressApi = opBean.getProgressApi();
        if (progressApi == null || StrUtil.isBlank(progressApi.getUrl())) {
            return;
        }

        String url = progressApi.getUrl();
        url = (String) El.eval(elContext, "`" + url + "`");
        Method method = Method.valueOf(progressApi.getMethod().name());

        logInfo("progressApi.method: " + progressApi.getMethod().name());
        logInfo("progressApi.url: " + url);

        HttpResponse response = HttpUtil.createRequest(method, url).execute();
        if (StrUtil.isNotBlank(progressApi.getExp())) {
            JSONObject httpResponse = getHttpResponse(response);
            if (httpResponse != null) {
                String progress = JSONPath.eval(httpResponse, progressApi.getExp()).toString();
                if (!ObjectUtil.equals(progress, curProgress)) {
                    progress(progress);
                }
            }
        }
    }

    public static String getLog(HttpOperatorBean opBean, ElContext elContext) {
        LogApiBean logApi = opBean.getLogApi();
        if (logApi == null || StrUtil.isBlank(logApi.getUrl())) {
            return null;
        }
        String url = logApi.getUrl();
        url = (String) El.eval(elContext, "`" + url + "`");
        Method method = Method.valueOf(logApi.getMethod().name());

        logInfo("logApi.method: " + logApi.getMethod().name());
        logInfo("logApi.url: " + url);

        HttpResponse response = HttpUtil.createRequest(method, url).execute();
        String resultLog = response.body();
        logInfo("==========算子执行日志开始==========");
        logInfo(resultLog);
        logInfo("==========算子执行日志结束==========");
        if (StrUtil.isNotBlank(logApi.getExp())) {
            JSONObject httpResponse = getHttpResponse(response);
            resultLog = JSONPath.eval(httpResponse, logApi.getExp()).toString();
        }
        return resultLog;
    }

    public static void getResult(HttpOperatorBean opBean, ElContext elContext) {
        ResultApiBean resultApi = opBean.getResultApi();
        if (resultApi == null || StrUtil.isBlank(resultApi.getUrl())) {
            return;
        }
        try {
            String url = resultApi.getUrl();
            url = (String) El.eval(elContext, "`" + url + "`");
            Method method = Method.valueOf(resultApi.getMethod().name());

            logInfo("resultApi.method: " + resultApi.getMethod().name());
            logInfo("resultApi.url: " + url);

            HttpResponse response = HttpUtil.createRequest(method, url).execute();

            List<ExpParamBean> resultParam = resultApi.getResultParam();
            JSONObject httpResponse = getHttpResponse(response);
            for (ExpParamBean expParamBean : resultParam) {
                if (StrUtil.isBlank(expParamBean.getExp())) continue;

                String paramName = expParamBean.getName();
                String paramValue = JSONPath.eval(httpResponse, expParamBean.getExp()).toString();
                // 输出结果
                logResult(paramName, paramValue);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            logError("结果参数抽取失败：" + ex.getMessage());
            failedExit();
        }
    }

    public static JSONObject getHttpResponse(HttpResponse response) {
        HttpResponseBean resBean = new HttpResponseBean();
        if (response.getStatus() != 200) {
            logError("请求响应失败：" + response.getStatus());
            logInfo("异常状态码: " + response.getStatus());
            logInfo(response.body());
            return null;
        }
        resBean.setStatus(String.valueOf(response.getStatus()));

        String body = response.body();

        JSONObject jsonObject = JSONObject.parseObject(body);
        resBean.setBody(jsonObject);
        Map<String, List<String>> headers = response.headers();
        for (String name : headers.keySet()) {
            List<String> list = headers.get(name);
            String value = list.get(0);
            resBean.getHeader().put(name, value);
        }
        return (JSONObject) JSONObject.toJSON(resBean);
    }

    /**
     * 判断路径是否为相对路径，并补全为绝对路径
     *
     * @param path    待检查的路径
     * @param workdir 工作目录
     * @return 补全后的绝对路径
     */
    public static String completeRelativePath(String path, String workdir) {
        Path pathObj = Paths.get(path);

        if (pathObj.isAbsolute()) {
            return path;
        } else {
            Path absolutePath = Paths.get(workdir).resolve(path);
            return absolutePath.toString();
        }
    }

}
